package com.jamal2362.apktool.util;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.os.Environment;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import com.jamal2362.apktool.ApktoolApplication;
import com.jamal2362.apktool.R;
import com.jamal2362.apktool.fragment.files.FileComparator;
import com.jamal2362.apktool.lexer.Packages;
import com.jamal2362.apktool.service.NotificationManager;
import com.jamal2362.apktool.service.Project;

import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import brut.androlib.ApkOptions;
import brut.util.OS;
import sun1.security.pkcs.PKCS8Key;

public class Settings {
	public static boolean lightTheme;
	public static boolean isSetChanged;
	public static String aapt;
	public static String framework_dir;
	public static PrivateKey privateKey;
	public static X509Certificate certificate;
	public static Typeface typeface;
	public static int fontSize;
	public static int appFontSize;
	public static boolean isEditorSetChanged;
	public static String projectPath;
	public static Project project;
	public static String output_directory;
	public static String homeDir;
	public static boolean analysis_all_smali;
	public static boolean wordwrap;
	public static boolean suggestion;
	public static String dateFormat;
	public static String appLanguage;
	public static String sysArch;
	public static boolean hiddenFiles;
	public static boolean roundIcons;
	public static boolean folderSize;
	public static boolean showErrors;
	public static int autoSave;
	public static boolean screenOn;
	public static String editorTheme;
	public static String editorThemePath;
	public static String aapt2;
	public static boolean useAapt2;
	public static String zipAlign;
	public static String previewsDir;
	public static String appCacheDir;
	public static boolean nonPrinting;
	public static boolean quickPanel;

	public static void init(ApktoolApplication application) {
		isSetChanged = false;
		isEditorSetChanged = false;
		sysArch = detArchName();
		lightTheme = getb(application, "light_theme", true);
		useAapt2 = getb(application, "use_aapt2", true);
		appFontSize = geti(application, "napp_fontsz", 16);
		fontSize = geti(application, "nfont_size", 14);
		autoSave = Integer.parseInt(gets(application, "auto_save", "15"));
		showErrors = getb(application, "show_errors", false);
		appLanguage = gets(application, "lang", "default");
		editorTheme = gets(application, "editor_theme", "default");
		hiddenFiles = getb(application, "hide_files", false);
		roundIcons = getb(application, "round_icons", false);
		folderSize = getb(application, "dirs_size", false);
		screenOn = getb(application, "screen_on", false);
		nonPrinting = getb(application, "non_printing_chars", true);
		quickPanel = getb(application, "quick_panel", true);
		homeDir = gets(application, "home_dir", Environment.getExternalStorageDirectory().getAbsolutePath());
		dateFormat = gets(application, "date_format", "yyyy/MM/dd HH:mm");
		appCacheDir = application.getCacheDir().getAbsolutePath();
		NotificationManager nm = new NotificationManager(application);
		project = new Project(nm, ContextCompat.getNoBackupFilesDir(application));
		copyFiles(application.getAssets(), application.getFilesDir());
		setPreviewsDir(application.getFilesDir());
		loadSettings(application);
	}

	public static void loadSettings(Context context) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
		loadApplicationSettings(sp, context);
		loadEditorSettings(sp, context);
		loadApkOptions(sp);
		int defaultCompator = sp.getInt("defaultCompator", 0);
		FileComparator.setDefaultAdapter(defaultCompator);
		ApkOptions o = ApkOptions.INSTANCE;
		if (Settings.useAapt2) {
			o.aaptPath = Settings.aapt2;
			o.aaptVersion = 2;
		} else {
			o.aaptPath = Settings.aapt;
			o.aaptVersion = 1;
		}
		o.frameworkFolderLocation = framework_dir;
	}

	private static void setPreviewsDir(File dir) {
		File prevDir = new File(dir, "thumbnails");
		if (prevDir.exists() && !prevDir.isDirectory()) {
			prevDir.delete();
			prevDir.mkdir();
		}
		if (!prevDir.exists()) {
			prevDir.mkdir();
		}
		previewsDir = prevDir.getAbsolutePath();
		autoCleanPreviews(7); // ???
	}

	public static void autoCleanPreviews(int dayOld) {
		File prevDir = new File(previewsDir);
		if (!prevDir.exists() || !prevDir.isDirectory()) {
			return;
		}
		File[] previews = prevDir.listFiles();
		if (previews != null) {
			long current = System.currentTimeMillis();
			for (File p : previews) {
				long days = TimeUnit.MILLISECONDS.toDays(current - p.lastModified());
				if (days >= dayOld) {
					p.delete();
				}
			}
		}
	}

	public static void remove(Context c, String n) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
		SharedPreferences.Editor e = sp.edit();
		e.remove(n);
		e.commit();
	}

	public static boolean getb(Context c, String n, boolean d) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
		return sp.getBoolean(n, d);
	}

	public static void setb(Context c, String n, boolean d) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
		SharedPreferences.Editor e = sp.edit();
		e.putBoolean(n, d);
		e.commit();
	}

	public static String gets(Context c, String n, String d) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
		return sp.getString(n, d);
	}

	public static void sets(Context c, String n, String d) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
		SharedPreferences.Editor e = sp.edit();
		e.putString(n, d);
		e.commit();
	}

	public static int geti(Context c, String n, int d) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
		return sp.getInt(n, d);
	}

	public static void seti(Context c, String n, int d) {
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
		SharedPreferences.Editor e = sp.edit();
		e.putInt(n, d);
		e.commit();
	}

	public static Typeface getTypeface(SharedPreferences sp, Context c) {
		Typeface tf;
		try {
			if (sp.getBoolean("use_monospace", true)) {
				tf =  Typeface.createFromAsset(c.getAssets(), "monospace.ttf");
			} else {
				tf = Typeface.DEFAULT;
			}
		} catch (Exception e) {
			tf = Typeface.DEFAULT;
		}
		return tf;
	}

	private static void loadEditorSettings(SharedPreferences sp, Context context) {
		int size = sp.getInt("nfont_size", 14);
		if (Settings.fontSize != size)
			isEditorSetChanged = true;
		Settings.fontSize = size;
		int as = Integer.parseInt(sp.getString("auto_save", "15"));
		if (Settings.autoSave != as)
			isEditorSetChanged = true;
		Settings.autoSave = as;
		boolean ww = sp.getBoolean("is_wordwrap", false);
		if (Settings.wordwrap != ww)
			isEditorSetChanged = true;
		Settings.wordwrap = ww;
		boolean sg = sp.getBoolean("suggestion_keyboard", false);
		if (Settings.suggestion != sg)
			isEditorSetChanged = true;
		Settings.suggestion = sg;
		boolean np = sp.getBoolean("non_printing_chars", true);
		if (Settings.nonPrinting != np)
			isEditorSetChanged = true;
		Settings.nonPrinting = np;
		boolean qp = sp.getBoolean("quick_panel", true);
		if (Settings.quickPanel != qp)
			isEditorSetChanged = true;
		Settings.quickPanel = qp;
		Typeface tp = Settings.getTypeface(sp, context);
		if (Settings.typeface != tp)
			isEditorSetChanged = true;
		Settings.typeface = tp;
	}

	private static void loadApplicationSettings(SharedPreferences sp, Context ctx) {
		Settings.screenOn = sp.getBoolean("screen_on", false);
		String appLanguage = sp.getString("lang", "default");
		if (!appLanguage.equals(Settings.appLanguage))
			isSetChanged = true;
		Settings.appLanguage = appLanguage;
		int fontsz = sp.getInt("napp_fontsz", 16);
		if (Settings.appFontSize != fontsz)
			isSetChanged = true;
		Settings.appFontSize = fontsz;
		boolean lightTheme = sp.getBoolean("light_theme", true);
		if (lightTheme != Settings.lightTheme)
			isSetChanged = true;
		Settings.lightTheme = lightTheme;
		boolean hfiles = sp.getBoolean("hide_files", false);
		if (hfiles != Settings.hiddenFiles)
			isSetChanged = true;
		Settings.hiddenFiles = hfiles;
		boolean ricons = sp.getBoolean("round_icons", false);
		if (ricons != Settings.roundIcons)
			isSetChanged = true;
		Settings.roundIcons = ricons;
		boolean fsize = sp.getBoolean("dirs_size", false);
		if (fsize != Settings.folderSize)
			isSetChanged = true;
		Settings.folderSize = fsize;
		boolean serr = sp.getBoolean("show_errors", false);
		if (serr != Settings.showErrors)
			isSetChanged = true;
		Settings.showErrors = serr;
		String dformat = sp.getString("date_format", "yyyy/MM/dd HH:mm");
		if (!dformat.equals(Settings.dateFormat))
			isSetChanged = true;
		Settings.dateFormat = dformat;
		String etheme = sp.getString("editor_theme", "default");
		if (!Settings.editorTheme.equals(etheme))
			isSetChanged = true;
		Settings.editorTheme = etheme;

		boolean aa2 = sp.getBoolean("use_aapt2", true);
		if (aa2 != Settings.useAapt2)
			isSetChanged = true;
		Settings.useAapt2 = aa2;

		Settings.projectPath = sp.getString("projectPath", null);
		if (Settings.projectPath == null) {
			setProjectPath("", ctx);
		} else {
			try {
				File pf = new File(projectPath);
				if (!pf.exists()) {
					setProjectPath("", ctx);
				} else {
					project.setProjectPath(projectPath);
				}
			} catch (Exception e) {
				setProjectPath("", ctx);
			}
		}
	}

	public static void setProjectPath(String projectPath, Context ctx) {
		Settings.projectPath = projectPath;
		project.setProjectPath(projectPath);
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(ctx);
		SharedPreferences.Editor e = sp.edit();
		e.putString("projectPath", projectPath);
		e.commit();
	}

	public static void copyFiles(AssetManager assets, File outDir) {
		try {
			copy_aapt(assets, outDir);
			copy_aapt2(assets, outDir);
			copy_zipalign(assets, outDir);
			copy_framework(assets, outDir);
			copy_theme(assets, outDir);
			loadKey(assets);
			Packages.loadDex(assets);
			Packages.reset();
		} catch (IOException e) {
			//this should not happen
		} catch (InvalidKeyException|CertificateException e) {

		}
	}

	private static void loadKey(AssetManager assets) throws IOException, InvalidKeyException, CertificateException {
		loadPrivateKey(assets);
		loadCert(assets);
	}

	private static void loadCert(AssetManager assets) throws IOException, CertificateException {
		InputStream cert = assets.open("key/testkey.x509.pem");
		certificate = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(cert);
		cert.close();
	}

	private static void loadPrivateKey(AssetManager assets) throws IOException, InvalidKeyException {
		InputStream key = assets.open("key/testkey.pk8");
		PKCS8Key pkcs8 = new PKCS8Key();
		pkcs8.decode(key);
		privateKey = pkcs8;
		key.close();
	}

	private static void copy_framework(AssetManager assets, File outDir) throws IOException {
		File framework = new File(outDir, "framework/1.apk");
		if (framework.exists() && framework.isFile()) {
			framework_dir = framework.getParent();
			return;
		}
		InputStream framework_in = assets.open("android.jar");
		File dir = framework.getParentFile();
		dir.mkdirs();
		OutputStream framework_out = new FileOutputStream(framework);
		IOUtils.copy(framework_in, framework_out);
		framework_in.close();
		framework_out.close();
		framework_dir = dir.getAbsolutePath();
	}

	private static void copy_aapt(AssetManager assets, File outDir) throws IOException {
		File aapt = new File(outDir, "bin/aapt");
		if (aapt.exists() && aapt.isFile()) {
			Settings.aapt = aapt.getAbsolutePath();
			aapt.setExecutable(true);
			return;
		}
		aapt.getParentFile().mkdirs();
		InputStream aapt_in = assets.open(Settings.sysArch.substring(0, 3) + "/aapt");
		OutputStream aapu_out = new FileOutputStream(aapt);
		IOUtils.copy(aapt_in, aapu_out);
		aapt_in.close();
		aapu_out.close();
		aapt.setExecutable(true);
		Settings.aapt = aapt.getAbsolutePath();
	}

	private static void copy_aapt2(AssetManager assets, File outDir) throws IOException {
		File aapt2 = new File(outDir, "bin/aapt2");
		if (aapt2.exists() && aapt2.isFile()) {
			Settings.aapt2 = aapt2.getAbsolutePath();
			aapt2.setExecutable(true);
			return;
		}
		aapt2.getParentFile().mkdirs();
		InputStream aapt2_in = assets.open(Settings.sysArch.substring(0, 3) + "/aapt2");
		OutputStream aapt2_out = new FileOutputStream(aapt2);
		IOUtils.copy(aapt2_in, aapt2_out);
		aapt2_in.close();
		aapt2_out.close();
		aapt2.setExecutable(true);
		Settings.aapt2 = aapt2.getAbsolutePath();
	}

	private static void copy_zipalign(AssetManager assets, File outDir) throws IOException {
		File zipalign = new File(outDir, "bin/zipalign");
		if (zipalign.exists() && zipalign.isFile()) {
			Settings.zipAlign = zipalign.getAbsolutePath();
			zipalign.setExecutable(true);
			return;
		}
		zipalign.getParentFile().mkdirs();
		InputStream zipalign_in = assets.open("bin/zipalign");
		OutputStream zipalign_out = new FileOutputStream(zipalign);
		IOUtils.copy(zipalign_in, zipalign_out);
		zipalign_in.close();
		zipalign_out.close();
		zipalign.setExecutable(true);
		Settings.zipAlign = zipalign.getAbsolutePath();
	}

	public static void copy_theme(AssetManager assets, File outDir) throws IOException {
		File theme = new File(outDir, "theme/_" + Settings.editorTheme + ".json");
		if (theme.exists() && theme.isFile()) {
			Settings.editorThemePath = theme.getAbsolutePath();
			return;
		}
		theme.getParentFile().mkdirs();
		InputStream theme_in = assets.open("themes/" + Settings.editorTheme + ".json");
		OutputStream theme_out = new FileOutputStream(theme);
		IOUtils.copy(theme_in, theme_out);
		theme_in.close();
		theme_out.close();
		Settings.editorThemePath = theme.getAbsolutePath();
	}

	private static void loadApkOptions(SharedPreferences sp) {
		ApkOptions o = ApkOptions.INSTANCE;
		o.copyOriginalFiles = sp.getBoolean("copyOriginalFiles", false);
		o.debugMode = sp.getBoolean("debug_mode", false);
		o.verbose = sp.getBoolean("verbose_mode", false);
		analysis_all_smali = sp.getBoolean("analysis_all_smali", false);
		String output_dir = sp.getString("output_directory", null);
		if (output_dir != null && output_dir.equals(""))
			output_dir = null;
		Settings.output_directory = output_dir;
	}

	public static void setOutputDirectory(String output_directory, Context ctx) {
		Settings.output_directory = output_directory;
		SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(ctx);
		SharedPreferences.Editor e = sp.edit();
		e.putString("output_directory", output_directory);
		e.commit();
	}

	private static String detArchName() {
		try {
			String arch = System.getProperty("os.arch");
			if (arch.contains("64")) {
				if (arch.contains("86"))
					return "x86_64";
				else
					return "arm64-v8a";
			} else {
				if (arch.contains("86"))
					return "x86";
				else
					return "armeabi-v7a";
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "armeabi-v7a";
	}

	public static void resetFiles(final AppCompatActivity act) {
		LayoutInflater inflater = act.getLayoutInflater();
		View view = inflater.inflate(R.layout.search_progress, null);
		TextView tview = view.findViewById(R.id.searchprogressTextView1);
		tview.setText(R.string.mplease_wait);
		final AlertDialog progress = new AlertDialog.Builder(act)
			.setView(view)
			.setCancelable(false)
			.create();
		progress.show();
		Runnable removeFiles = new Runnable()
		{
			@Override
			public void run() {
				File dir = act.getFilesDir();
				if (dir.isDirectory()) {
					File[] children = dir.listFiles();
					for (int i = 0; i < children.length; i++) {
						if (children[i].isDirectory())
							OS.rmdir(children[i]);
						else
							children[i].delete();
					}
				}
				copyFiles(act.getAssets(), dir);
				setPreviewsDir(dir);
				progress.cancel();
				TextUtil.st(act, act.getString(R.string.complete));
			}
		};
		new Handler().postDelayed(removeFiles, 100);
	}
}
